package com.heima.schedule.service;

import com.alibaba.fastjson.JSON;
import com.heima.common.schedule.CacheService;
import com.heima.model.schedule.dto.Task;
import com.heima.model.schedule.pojos.Taskinfo;
import com.heima.model.schedule.pojos.TaskinfoLogs;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.Date;
import java.util.List;
import java.util.Set;

@Service
@Slf4j
public class TaskServiceImpl implements TaskService{
    @Autowired
    private TaskinfoService taskinfoService;

    @Autowired
    private TaskinfoLogsService taskinfoLogsService;

    @Autowired
    private CacheService cacheService;

    @Override
    @Transactional(rollbackFor = RuntimeException.class)
    public Long addTask(Task task) {
        //判空
        if (task==null){
            log.warn("参数为空");
            return null;
        }
        //插入taskinfo表
        Taskinfo taskinfo=new Taskinfo();
        BeanUtils.copyProperties(task,taskinfo);
        taskinfo.setExecuteTime(new Date(task.getExecuteTime()));
        boolean aboolean = taskinfoService.save(taskinfo);
        if (!aboolean){
            log.warn("taskinfo表插入失败");
            throw new RuntimeException("taskinfo表插入失败");

        }
        //插入taskinfoLog表
        TaskinfoLogs tasklog = new TaskinfoLogs();
        BeanUtils.copyProperties(task,tasklog);
        tasklog.setTaskId(taskinfo.getTaskId());
        tasklog.setExecuteTime(new Date(task.getExecuteTime()));
        tasklog.setStatus(1);
        boolean bboolean = taskinfoLogsService.save(tasklog);
        if (!bboolean){
            log.warn("taskinfoLog表插入失败");
            throw new RuntimeException("taskinfoLog表插入失败");
        }
        //判断任务执行时间是否小于当前时间
        //任务执行时间

        task.setTaskId(taskinfo.getTaskId());

        long executeTime = task.getExecuteTime();
        //当前时间
        long currentTime = System.currentTimeMillis();
        if (executeTime<=currentTime){
            cacheService.lLeftPush("TOPIC", JSON.toJSONString(task));
            return taskinfo.getTaskId();
        }
        //判断任务执行时间是否小于未来五分钟
        //未来时间
        long Time = currentTime + (5 * 60 * 1000);
        if (executeTime<=Time){
            cacheService.zAdd("FUTURE",JSON.toJSONString(task),task.getExecuteTime());
        }
        //返回任务id
        return taskinfo.getTaskId();
    }

    @Override
    @Transactional
    public Boolean cancelTask(Long taskId) {
        //判空
        if (taskId==null){
            log.warn("参数为空");
            return false;
        }
        //根据id删除
        boolean taskinfoResult = taskinfoService.removeById(taskId);
        if (!taskinfoResult){
            log.warn("删除失败");
            return false;
        }
        //根据id查询
        TaskinfoLogs logs = taskinfoLogsService.getById(taskId);
        logs.setStatus(2);
        //修改
        boolean logResult = taskinfoLogsService.updateById(logs);
        if (!logResult){
            log.warn("更新失败");
            throw new RuntimeException();
        }
        //属性拷贝
        Task task = new Task();
        BeanUtils.copyProperties(logs,task);
        task.setExecuteTime(logs.getExecuteTime().getTime());
        //根据task删除执行队列的任务
        cacheService.lRemove("TOPIC",0,JSON.toJSONString(task));
        //根据task删除未来队列中的任务
        cacheService.zRemove("FUTURE",JSON.toJSONString(task));



        return true;
    }

    @Override
    @Transactional(rollbackFor = RuntimeException.class)
    public Task pullTask() {
        //指定topic队列，进行消费
        String topic = cacheService.lRightPop("TOPIC");
        if (StringUtils.isEmpty(topic)){
            return null;
        }
        Task task = JSON.parseObject(topic, Task.class);
        if (task ==null){
            log.warn("没有可执行任务");
            return null;
        }
        //任务id


        //删除
        boolean removeById = taskinfoService.removeById(task.getTaskId());
        if (!removeById){
            return null;
        }
        Long executeTime = task.getExecuteTime();
        TaskinfoLogs logs = new TaskinfoLogs();
        logs.setExecuteTime(new Date(executeTime));
        BeanUtils.copyProperties(task,logs);
        logs.setStatus(1);
        boolean logResult = taskinfoLogsService.updateById(logs);
        if (!logResult ){
            log.warn("日志更新失败");
            throw  new RuntimeException("日志更新失败");
        }


        //把json字符串转成task
        return task;
    }

    @Override
    //@Scheduled(fixedRate = 1000)
    public void refresh() {
        String token = cacheService.tryLock("FUTURE_TASK_SYNC", 1000 * 30);
        if (StringUtils.isEmpty(token)){
            return;
        }
        //获取从0到当前时间下FUTURE队列下的所有任务
        Set<String> future = cacheService.zRangeByScore("FUTURE", 0, System.currentTimeMillis());
        if (CollectionUtils.isEmpty(future)){
            return;
        }
        //调用方法把任务传递到执行队列
        cacheService.refreshWithPipeline("FUTURE","TOPIC",future);
    }

    @Override
   // @Scheduled(fixedRate = 5*60*1000)
    public void initData() {
        //清楚redis的数据
        clear();
        //读取mysql里面的任务
        List<Taskinfo> taskinfos = taskinfoService.list();
        if (!CollectionUtils.isEmpty(taskinfos)) {
            for (Taskinfo taskinfo : taskinfos) {
                if (taskinfo==null){
                    continue;
                }
                Task task = new Task();
                BeanUtils.copyProperties(taskinfo,task);
                long executeTime = task.getExecuteTime();
                long curruteTime= System.currentTimeMillis();
                long futureTime=curruteTime+(5*60*1000);
                //任务执行时间是否小于当前时间
                if (executeTime<=curruteTime){
                    cacheService.lLeftPush("TOPIC",JSON.toJSONString(task));
                    //任务执行时间是否 小于未来时间五分钟
                }else if (executeTime<=futureTime){
                    cacheService.zAdd("FUTURE",JSON.toJSONString(task),executeTime);
                }
            }
        }
    }

    private void clear(){
        cacheService.delete("TOPIC");
        cacheService.delete("FUTURE");
    }


}
